5
  • 这篇文章是我自己的一个学习总结,并不是非常详细,结合给出的链接可以有更细致的认识
  • 先介绍几个概念,便于理解

关于堆和栈(作为内存区域来说)

  • 堆(heap):存放object、array、function等不确定内存大小的数据存储;
  • 栈(stack):存放基本数据类型以及引用数据类型指向堆中的数据的指针,具有具体大小的数据结构,存取速度快;

调用栈(作为一种代码运行机制)

  • call stack(调用栈)指的是函数调用运行的机制,具体参考该链接:javascrip调用栈

事件循环机制(event loop)

参考:js事件循环机制

  • 存在整个javascript脚本执行期间
  • 作用:将任务队列的中可以执行的函数压入调用栈中

任务队列(task queue)

任务队列主要分为两种:

  1. 宏任务(macro task):在新标准中叫task

    宏任务主要包括:script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering
  2. 微任务(micro task):在新标准中叫jobs

    微任务主要包括:process.nextTick, Promise, Object.observe(已废弃), MutationObserver(html5新特性)

以上提到的不只有浏览器方法,还有nodejs的方法,这里不具体说明了

执行特点:

  • 每当调用栈为空时,事件循环机制会将一个宏任务队列中任务压入调用栈中
  • 以空的调用栈为起点的话,先执行所有宏任务,再执行所有微任务,然后调用栈又为空,这样一次可以看作一个单元,之后就是一直在循环执行这样单元

分解执行过程:

  1. 执行所有调用栈中的宏任务
  2. 宏任务执行过程中产生的微任务加入到微任务队列
  3. 宏任务执行完立刻执行所有微任务队列中的任务
  4. 以上执行完毕,检查渲染,GUI线程接管渲染
  5. 渲染完毕后,js线程接管,开启下一次事件循环(每一次事件循环(script不包括),只处理一个宏任务),执行下一次宏任务(任务队列中取)

不好理解的地方

  • 以上过程(无论宏任务还是微任务执行)中产生的宏任务进入宏任务队列等待,进入后面的循环执行,不在当次循环中被执行
  • 但是以上过程中(包括微任务)产生的微任务又会被立刻放到当次循环的微任务队列后面按顺序执行

以上两句可能有点绕,可以参考上面循环机制的链接,有相关图解。

换个说法:微任务优先于当前调用栈产生的宏任务被执行

如果能理解下面这段代码的执行过程应该就基本理解任务队列的执行过程了:

setTimeout(() => {
    console.log('1')

    new Promise((resolve) => {
        resolve()
    }).then(() => {
        console.log('2')
    })
}, 0);

setTimeout(() => {
    console.log('3')
}, 0);

new Promise((resolve) => {
    resolve()
}).then(() => {
    console.log('4')

    new Promise((resolve) => {
        resolve()
    }).then(() => {
        console.log('5')
    })

    setTimeout(() => {
        console.log('6')
    }, 0);
})

new Promise((resolve) => {
    resolve()
}).then(() => {
    console.log('7')
})

// 输出顺序为: 4,7,5,1,2,3,6

木木俞
192 声望5 粉丝

佛系前端